package com.jeff.android.lab.module.annotation.runtime;

import android.app.Activity;
import android.view.View;

import com.jeff.android.lab.module.annotation.runtime.annotations.ContentView;
import com.jeff.android.lab.module.annotation.runtime.annotations.EventBase;
import com.jeff.android.lab.module.annotation.runtime.annotations.InjectView;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ViewInject {

    public static void inject(Activity activity) {
        injectLayout(activity);

        injectViews(activity);

        injectEvents(activity);
    }

    private static void injectLayout(Activity activity) {
        Class<?> clazz = activity.getClass();
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if (contentView != null) {
            int layoutId = contentView.value();
            if (layoutId > 0) {
                try {
                    Method method = clazz.getMethod("setContentView", int.class);
                    method.invoke(activity, layoutId);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void injectViews(Activity activity) {
        Class<?> clazz = activity.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            InjectView injectView = field.getAnnotation(InjectView.class);
            if (injectView != null) {
                int viewId = injectView.value();
                if (viewId > 0) {
                    try {
                        Method method = clazz.getMethod("findViewById", int.class);
                        Object view = method.invoke(activity, viewId);
                        field.setAccessible(true);
                        field.set(activity, view);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private static void injectEvents(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        // 获取类的所有方法
        Method[] methods = clazz.getDeclaredMethods();
        // 遍历方法
        for (Method method : methods) {
            // 获取方法的注解(一个方法可能有多个注解)
            Annotation[] annotations = method.getAnnotations();
            // 遍历方法的注解
            for (Annotation annotation : annotations) {
                // 注解类型
                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (annotationType != null) {
                    EventBase eventBase = annotationType.getAnnotation(EventBase.class);
                    if (eventBase != null) {
                        String listenerSetter = eventBase.listenerSetter();
                        Class<?> listenerType = eventBase.listenerType();
                        String callbackListener = eventBase.callbackListener();

                        ListenerInvocationHandler handler = new ListenerInvocationHandler(activity);
                        handler.addMethod(callbackListener, method);

                        Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler);

                        try {
                            Method valueMethod = annotationType.getDeclaredMethod("value");
                            int[] viewIds = (int[]) valueMethod.invoke(annotation);
                            for (int viewId : viewIds) {
                                // 获取当前Activity 的 View
                                View view = activity.findViewById(viewId);
                                // 获取指定的方法
                                Method setter = view.getClass().getMethod(listenerSetter, listenerType);
                                // 执行方法
                                setter.invoke(view, listener);
                            }

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

}
